﻿using System;
using System.Diagnostics;
using System.Windows.Forms;

namespace Be.Windows.Forms
{
    public partial class HexBox
    {
        private void VScrollBar_Scroll(object sender, ScrollEventArgs e)
        {
            switch (e.Type)
            {
                case ScrollEventType.Last:
                    break;

                case ScrollEventType.EndScroll:
                    break;

                case ScrollEventType.SmallIncrement:
                    PerformScrollLineDown();
                    break;

                case ScrollEventType.SmallDecrement:
                    PerformScrollLineUp();
                    break;

                case ScrollEventType.LargeIncrement:
                    PerformScrollPageDown();
                    break;

                case ScrollEventType.LargeDecrement:
                    PerformScrollPageUp();
                    break;

                case ScrollEventType.ThumbPosition:
                    long lPos = FromScrollPos(e.NewValue);
                    PerformScrollThumpPosition(lPos);
                    break;

                case ScrollEventType.ThumbTrack:
                    // to avoid performance problems use a refresh delay implemented with a timer
                    if (_thumbTrackTimer.Enabled) // stop old timer
                        _thumbTrackTimer.Enabled = false;

                    // perform scroll immediately only if last refresh is very old
                    int currentThumbTrack = System.Environment.TickCount;
                    if (currentThumbTrack - _lastThumbtrack > THUMPTRACKDELAY)
                    {
                        PerformScrollThumbTrack(null, null);
                        _lastThumbtrack = currentThumbTrack;
                        break;
                    }

                    // start thumbtrack timer
                    _thumbTrackPosition = FromScrollPos(e.NewValue);
                    _thumbTrackTimer.Enabled = true;
                    break;

                case ScrollEventType.First:
                    break;

                default:
                    break;
            }

            e.NewValue = ToScrollPos(_scrollVpos);
        }

        /// <summary>
        /// Performs the thumbtrack scrolling after an delay.
        /// </summary>
        private void PerformScrollThumbTrack(object sender, EventArgs e)
        {
            _thumbTrackTimer.Enabled = false;
            PerformScrollThumpPosition(_thumbTrackPosition);
            _lastThumbtrack = Environment.TickCount;
        }

        /// <summary>
        /// 刷新控件
        /// </summary>
        public override void Refresh()
        {
            DestroyCaret();
            UpdateRectanglePositioning();
            UpdateVisibilityBytes();
            CreateCaret();
            UpdateCaret();
            Invalidate();
            base.Refresh();
        }

        private void UpdateScrollSize()
        {
            Debug.WriteLine("UpdateScrollSize()", "HexBox");

            // calc scroll bar info
            if (VScrollBarVisible && _byteProvider != null && _byteProvider.Length > 0 && _iHexMaxHBytes != 0)
            {
                long scrollmax = (long)Math.Ceiling((double)(_byteProvider.Length + 1) / (double)_iHexMaxHBytes - (double)_iHexMaxVBytes);
                scrollmax = Math.Max(0, scrollmax);

                long scrollpos = _startByte / _iHexMaxHBytes;

                if (scrollmax < _scrollVmax)
                {
                    /* Data size has been decreased. */
                    if (_scrollVpos == _scrollVmax)
                        /* Scroll one line up if we at bottom. */
                        PerformScrollLineUp();
                }

                if (scrollmax == _scrollVmax && scrollpos == _scrollVpos)
                    return;

                _scrollVmin = 0;
                _scrollVmax = scrollmax;
                _scrollVpos = Math.Min(scrollpos, scrollmax);
                UpdateVScroll();
            }
            else if (VScrollBarVisible)
            {
                // disable scroll bar
                _scrollVmin = 0;
                _scrollVmax = 0;
                _scrollVpos = 0;
                UpdateVScroll();
            }
        }

        private void UpdateVScroll()
        {
            Debug.WriteLine("UpdateVScroll()", "HexBox");

            int max = ToScrollMax(_scrollVmax);

            if (max > 0)
            {
                _vScrollBar.Minimum = 0;
                _vScrollBar.Maximum = max;
                _vScrollBar.Value = ToScrollPos(_scrollVpos);
                _vScrollBar.Visible = true;
            }
            else
            {
                _vScrollBar.Visible = false;
            }
        }

        private int ToScrollPos(long value)
        {
            int max = 65535;

            if (_scrollVmax < max)
                return (int)value;
            else
            {
                double valperc = value / (double)_scrollVmax * 100;
                int res = (int)Math.Floor((double)max / 100 * valperc);
                res = (int)Math.Max(_scrollVmin, res);
                res = (int)Math.Min(_scrollVmax, res);
                return res;
            }
        }

        private long FromScrollPos(int value)
        {
            int max = 65535;
            if (_scrollVmax < max)
            {
                return (long)value;
            }
            else
            {
                double valperc = (double)value / (double)max * (double)100;
                long res = (int)Math.Floor((double)_scrollVmax / (double)100 * valperc);
                return res;
            }
        }

        private int ToScrollMax(long value)
        {
            long max = 65535;
            if (value > max)
                return (int)max;
            else
                return (int)value;
        }

        private void PerformScrollToLine(long pos)
        {
            if (pos < _scrollVmin || pos > _scrollVmax || pos == _scrollVpos)
                return;

            _scrollVpos = pos;

            UpdateVScroll();
            UpdateVisibilityBytes();
            UpdateCaret();
            Invalidate();
        }

        private void PerformScrollLines(int lines)
        {
            long pos;
            if (lines > 0)
            {
                pos = Math.Min(_scrollVmax, _scrollVpos + lines);
            }
            else if (lines < 0)
            {
                pos = Math.Max(_scrollVmin, _scrollVpos + lines);
            }
            else
            {
                return;
            }

            PerformScrollToLine(pos);
        }

        private void PerformScrollLineDown()
        {
            PerformScrollLines(1);
        }

        private void PerformScrollLineUp()
        {
            PerformScrollLines(-1);
        }

        private void PerformScrollPageDown()
        {
            PerformScrollLines(_iHexMaxVBytes);
        }

        private void PerformScrollPageUp()
        {
            PerformScrollLines(-_iHexMaxVBytes);
        }

        private void PerformScrollThumpPosition(long pos)
        {
            // Bug fix: Scroll to end, do not scroll to end
            int difference = (_scrollVmax > 65535) ? 10 : 9;

            if (ToScrollPos(pos) == ToScrollMax(_scrollVmax) - difference)
                pos = _scrollVmax;
            // End Bug fix

            PerformScrollToLine(pos);
        }

        /// <summary>
        /// Scrolls the selection start byte into view
        /// </summary>
        public void ScrollByteIntoView()
        {
            Debug.WriteLine("ScrollByteIntoView()", "HexBox");

            ScrollByteIntoView(_bytePos);
        }

        /// <summary>
        /// Scrolls the specific byte into view
        /// </summary>
        /// <param name="index">the index of the byte</param>
        public void ScrollByteIntoView(long index)
        {
            Debug.WriteLine("ScrollByteIntoView(long index)", "HexBox");

            if (_byteProvider == null || _keyInterpreter == null)
                return;

            if (index < _startByte)
            {
                long line = (long)Math.Floor((double)index / _iHexMaxHBytes);
                PerformScrollThumpPosition(line);
            }
            else if (index > _endByte)
            {
                long line = (long)Math.Floor((double)index / _iHexMaxHBytes);
                line -= _iHexMaxVBytes - 1;
                PerformScrollThumpPosition(line);
            }
        }

    }
}
